Autor: Alejandro Higuera Castro
Fecha: 9 de julio de 2020
La demostración del módulo consta de 6 partes:
Como proyecto para el curso de Introducción a las Ciencias de la Computación en el semestre 2020-1S desarrollé un notebook en Google Colaboratory (iPython) para el análisis y modificación de audio en un formato wav, que viene acompañado del módulo ColabAudioProcessing para proveer un diseño modular al Notebook, el cuál está disponible para su instalación desde pip.
El sonido en el mundo físico son vibraciones que se propagan viajando por el aire o otro medio y que pueden ser escuchadas por el oído de una persona o un animal. Por otra parte, en el mundo diigtal el sonido son codificaciones de señales eléctricas que representan una onda sonora, parte de ello tenemos procesos como el muestreo y la cuantificación digital de la señal eléctrica, esto nos plantea problemáticas como su edición, transporte y reproducción en medios digitales.
Existen múltiples formatos digitales para el almacenamiento de sónido, algunos de los más conocidos .m4a, .mp3, .ogg, .aif, .wma, .wav, entre otros. En este proyecto buscamos manipular en particular el formato .wav que se caracteriza por la aseucncia de compresión en sus datos, su excelente calidad y su amplio soporte para cai todos los códes de audio actuales.
El módulo ColabAudioProcessing provee herramientas básicas y facilidades para la edición y análisis de audio en formato .wav. Puede ser instalado con el instalador de Paquetes de Python (PIP), dado que se encuentra publicado en el Índice de paquetes de Python pipy.org. Este integra las librerias Numpy, Scipy y Matplotlib para desplegar más de 10 funciones integradas que son muy útiles para la manipulación de archivos en el formato .wav, en este notebook es posible ver la demostración de la mayoría de estas funciones.
El notebook fue diseñado bajo los parámetros de diseño modular, pensando en un código limpio, entendible y fácil de reutilizar. Para ello se creó un modulo externo para el fácil despliegue de las funciones necesarias para el proyecto. pypi.org/project/ColabAudioProcessing
El módulo puede ser cargado desde el Notebook o con PIP.
!pip install ColabAudioProcessing
from ColabAudioProcessing.audio import *
#Código del módulo
from scipy.io import wavfile
import numpy as np
import IPython.display as ipd
from scipy.fftpack import *
import matplotlib.pyplot as plt
import math
def playAudio(file):
"""
Muestra en pantalla el reproductor de iPython Display para un archivo de
formato .wav.
Parámetros
----------
file: string
Nombre del archivo en formato .wav que contiene audio en formato
mono o estéreo.
Retorna
----------
Reproductor en pantalla de iPython con el audio estipulado
"""
return ipd.Audio(file)
def ReadAudio(file):
"""
Retorna la tasa de muestras por minuto y la matriz con los datos del audio}
en formato mono o estéreo.
Parámetros
----------
file: string
Nombre del archivo en formato .wav que contiene audio en formato
mono o estéreo.
Retorna
--------
List:
[rate,data]
rate: int
Muestras por segundo
data: numpy ndarray
Matriz con audio en mono o estéreo
"""
rate,data=wavfile.read(file)
return [rate,data]
def WriteAudio(filename,rate,matrix):
"""
Escribe un archivo de audio .wav a partir de una matriz numpy con los datos
del audio en mono o estéreo y la tasa de muestras por segundo.
Parámetros
----------
filename: string
Nombre del archivo de salida .wav.
matrix: numpy ndarray
Matriz con audio en mono o estéreo.
rate: int
Tasa de muestras por minuto del audio.
Retorna
--------
Archivo de formato wav con nombre establecido por el usuario <filename>.
"""
wavfile.write(filename,rate,matrix)
def Speed_Rep(input_filename,speed,output_filename):
"""
Muestra en pantalla el reproductor de audio y guarda el audio con la
velocidad dada por el usuario para el archivo .wav estipulado.
Parámetros
----------
input_filename: string
Nombre o localización/path del archivo .wav de entrada.
speed: float
Velocidad con la que se va a reproducir el audio de destino.
output_filename: string
Nombre o localización/path del archivo .wav de salida
Retorna
----------
Reproductor en pantalla de iPython con el audio con la velocidad deseada.
"""
rate,data=ReadAudio(input_filename)
WriteAudio(output_filename,int(rate*speed),data)
print(f"El archivo se guardo con éxito como {output_filename}")
return playAudio(output_filename)
def Inverse_Rep(input_filename,output_filename):
"""
Muestra en pantalla el reproductor de audio y guarda el audio reproducido
desde atrás en el archivo .wav estipulado.
Parámetros
----------
input_filename: string
Nombre o localización/path del archivo .wav de entrada.
output_filename: string
Nombre o localización/path del archivo .wav de salida
Retorna
----------
Reproductor en pantalla de iPython con el audio hacia atrás.
"""
rate,data=ReadAudio(input_filename)
#Convertimos a mono el audio original
data=ConvertToMono(data)
#Leemos la matriz desde atrás usando la notación de slicing de listas
WriteAudio(output_filename,rate,data[::-1])
print(f"El archivo se guardo con éxito como {output_filename}")
return playAudio(output_filename)
def ConvertToMono(data):
"""
Retorna un array de Numpy con la matriz de audio convertida a mono con el
mismo dtype de Numpy que el original.
Parámetros
----------
data: numpy ndarray
Matriz de Numpy que contiene audio en formato mono o estéreo.
Retorna
----------
mono: numpy ndarray
Matriz de Numpy que contiene audio en mono.
"""
#Se procede a leer el audio
if len(data.shape)==1:
canales=1
else:
canales=data.shape[1]
if canales==1:
mono=data
#En caso de que el audio sea de formato estéreo procede a su conversión
elif canales==2:
mono=[]
stereo_dtype=data.dtype
#Se obtienen los vectores correspondientes a cada canal de audio
l=data[:,0]
r=data[:,1]
#Se suma cada canal de audio para obtener uno solo
for i in range(len(data)):
d=(l[i]/2)+(r[i]/2)
mono.append(d)
mono=np.array(mono,dtype=stereo_dtype)
return mono
def Lowpass(data,alpha):
"""
Filtro exponencial EMA de paso bajo que recibe una matriz con audio en
mono y retorna una matriz con audio en mono del mismo tipo con el Filtro
aplicado.
Parámetros
----------
data: Matriz Numpy ndarray
Matriz con los datos de un audio mono.
alpha: float
Factor entre 0 y 1 que determina el suavizado y aplicación del filtro.
Retorna
----------
filtered: numpy ndarray
Matriz de Numpy que contiene audio en mono filtrado, con el mismo dtype
del original.
"""
f0=alpha*data[0]
filtered=[f0]
for i in range (1,len(data)):
#y[i] := α * x[i] + (1-α) * y[i-1]
f=alpha*data[i]+(1-alpha)*filtered[i-1]
filtered.append(f)
filtered=np.array(filtered,dtype=data.dtype)
return filtered
def Highpass(data,alpha):
"""
Filtro exponencial EMA de paso alto que recibe una matriz con audio en
mono y retorna una matriz con audio en mono del mismo tipo con el Filtro
aplicado.
Parámetros
----------
data: Matriz Numpy ndarray
Matriz con los datos de un audio mono.
alpha: float
Factor entre 0 y 1 que determina el suavizado y aplicación del filtro.
Retorna
----------
filtered: numpy ndarray
Matriz de Numpy que contiene audio en mono filtrado, con el mismo dtype
del original.
"""
f_Lowpass=Lowpass(data,alpha)
filtered=[]
for i in range(len(data)):
f=data[i]-f_Lowpass[i]
filtered.append(f)
filtered=np.array(filtered,dtype=data.dtype)
return filtered
def Frequency_Cutoff(type,frequency,input_filename,output_filename):
"""
Aplica el filtro exponencial EMA de acuerdo al tipo especificado por el
usuario (Lowpass o Highpass).
Parámetros
----------
type: string (low or high)
Tipo de filtro (Paso bajo o paso alto).
frequency: float
Frecuencia (Hz) de corte para aplicación de filtro.
input_filename: string
Nombre o localización/path del archivo .wav de entrada.
output_filename: string
Nombre o localización/path del archivo .wav de salida
Retorna
----------
filtered: numpy ndarray
Archivo de formato wav con nombre establecido por el usuario
<output_filename>.
"""
#Relación entre la frecuencia de corte y el parámetro alpha
rate,data=ReadAudio(input_filename)
dt=1/rate
alpha=(2*math.pi*dt*frequency)/((2*math.pi*dt*frequency)+1)
print(f"α={alpha}")
if type=="low":
data_f=Lowpass(data,alpha)
elif type=="high":
data_f=Highpass(data,alpha)
WriteAudio(output_filename,rate,data_f)
print(f"El archivo se guardo con éxito como {output_filename}")
def Combinar_Audios(audio1,audio2,output_filename):
"""
Muestra en pantalla el reproductor de audio y guarda el audio que combina
los dos audios de entrada.
Parámetros
----------
audio1: string
Nombre o localización/path del archivo .wav de entrada.
audio2: string
Nombre o localización/path del archivo .wav de entrada.
output_filename: string
Nombre o localización/path del archivo .wav de salida
Retorna
----------
Reproductor en pantalla de iPython con el audio que comnbina los audios de
entrada.
"""
rate_1,data_1=ReadAudio(audio1)
rate_2,data_2=ReadAudio(audio2)
if len(data_1)>len(data_2):
base_data=data_1.copy()
insert_data=data_2.copy()
else:
base_data=data_2.copy()
insert_data=data_1.copy()
for i in range (0,int(len(insert_data))):
base_data[i]=base_data[i]/2+insert_data[i]/2
WriteAudio(output_filename,(rate_1+rate_2)//2,base_data)
print(f"El archivo se guardo con éxito como {output_filename}")
return playAudio(output_filename)
def FFT_Graphing(Graph_Title,data_1,rate_1,audio1_title,data_2,rate_2,audio2_title):
"""
Grafica la transformada de fourier de dos audios, donde el eje x se
muestra como la frecuencia en Hertz y el eje y la amplitud. Esto permite
comparar de manera objetiva dos audios en su dominio frecuencia.
Parámetros
----------
Graph_Title: string
Título de la gráfica.
data_1: numpy ndarray
Matriz con audio en mono.
rate_1: int
Muestras por segundo del audio.
audio1_title: string
Nombre a mostrar en la gráfica.
data_2: numpy ndarray
Matriz con audio en mono.
rate_2: int
Muestras por segundo del audio.
audio2_title: string
Nombre a mostrar en la gráfica.
Retorna
--------
Gráfico de Matplotlib con la Transformada Rápida de Fourier de los audios de
entrada.
"""
plt.title(Graph_Title)
plt.xlabel("Frecuencia (Hz)")
plt.ylabel("Amplitud")
fft_data_1=abs(fft(data_1))
frecs_1=fftfreq(len(fft_data_1),(1/rate_1))
x1=frecs_1[:(len(fft_data_1)//2)]
y1=fft_data_1[:(len(fft_data_1)//2)]
fft_data_2=abs(fft(data_2))
frecs_2=fftfreq(len(fft_data_2),(1/rate_2))
x2=frecs_2[:(len(fft_data_2)//2)]
y2=fft_data_2[:(len(fft_data_2)//2)]
plt.plot(x1,y1,color="r",label=audio1_title)
plt.plot(x2,y2,color="g",label=audio2_title)
plt.legend(loc='upper right', borderaxespad=0.)
plt.show()
def AudioGraphing(Graph_Title,data_1,rate_1,audio1_title,data_2,rate_2,audio2_title):
"""
Grafica un audio/señal en el dominio tiempo, en el eje y se muestra la
señal y en el eje x el tiempo.
Parámetros
----------
Graph_Title: string
Título de la gráfica.
data_1: numpy ndarray
Matriz con audio en mono.
rate_1: int
Muestras por segundo del audio.
audio1_title: string
Nombre a mostrar en la gráfica.
data_2: numpy ndarray
Matriz con audio en mono.
rate_2: int
Muestras por segundo del audio.
audio2_title: string
Nombre a mostrar en la gráfica.
Retorna
--------
Gráfico de Matplotlib con los audios de entrada, en el eje x la amplitud
y en el eje y el tiempo en segundos.
"""
plt.title(Graph_Title)
plt.xlabel('Tiempo (s)')
plt.ylabel('Amplitud')
data_1=ConvertToMono(data_1)
tiempo_1=np.arange(len(data_1))/float(rate_1)
data_2=ConvertToMono(data_2)
tiempo_2=np.arange(len(data_2))/float(rate_2)
plt.fill_between(tiempo_1,data_1,color='b',label=audio1_title)
plt.fill_between(tiempo_2,data_2,color='m',label=audio2_title)
plt.legend(loc='upper right', borderaxespad=0.)
plt.show()
def AdjustVolume(input_filename,volume,output_filename):
"""
Muestra en pantalla el reproductor de audio y guarda el audio con la
velocidad dada por el usuario para el archivo .wav estipulado.
Parámetros
----------
input_filename: string
Nombre o localización/path del archivo .wav de entrada.
volume: float
Porcentaje de volumen del audio de salida.
output_filename: string
Nombre o localización/path del archivo .wav de salida
Retorna
----------
Reproductor en pantalla de iPython con el audio con el volumen deseado.
"""
rate,data=ReadAudio("sweet.wav")
#Convertirlo a mono, hace menos pesado y rápido de procesar el audio
data=ConvertToMono(data)
adjusted=[]
#Multiplicamos la amplitud actual por el factor de aumento deseado
for i in range(len(data)):
adjust=(volume/100)*data[i]
adjusted.append(adjust)
adjusted=np.array(adjusted,dtype=data.dtype)
WriteAudio(output_filename,rate,adjusted)
print(f"El archivo se guardo con éxito como {output_filename}")
return playAudio(output_filename)
!wget https://raw.githubusercontent.com/DarkNightSoldier/AudioProcessing/master/Happy.wav
!wget https://raw.githubusercontent.com/DarkNightSoldier/AudioProcessing/master/sweet.wav
!wget https://raw.githubusercontent.com/DarkNightSoldier/AudioProcessing/master/hfnoise.wav
Para reducir la velocidad del audio debemos reducir el número de muestras por segundo del audio.
Velocidad = 0.9 #@param {type:"slider", min:0.4, max:1, step:0.05}
Speed_Rep("Happy.wav",Velocidad,"slow.wav")
Para aumentar la velocidad del audio debemos aumentar el número de muestras por segundo del audio.
Velocidad = 1.65 #@param {type:"slider", min:1, max:5, step:0.05}
Speed_Rep("Happy.wav",Velocidad,"fast.wav")
Para reproducir el audio hacia atrás basta con leer la matriz desde atrás hacia adelante, conservando la misma tasa de muestras por segundo.
Inverse_Rep("Happy.wav","inverse.wav")
Este tipo de filtro se caracteriza por el paso de las frecuencias más bajas y la atenuación de las frecuencias más altas, lo que lo posiciona como útil para la disminución del ruido de alta frecuencia.
El filtro EMA de paso bajo consiste en obtener un valor filtrado a partir de la aplicación de la siguiente expresión con cada uno de los datos de la matriz del audio mono:
$y[i]= \alpha \cdot x[i] + (1-\alpha) \cdot y[i-1]$
Donde:
Este tipo de filtro se caracteriza por el paso de las frecuencias más altas y la atenuación de las frecuencias más bajas, lo que lo posiciona como útil para la disminución del ruido de baja frecuencia.
El filtro EMA de paso alto consiste en obtener un valor filtrado a partir de la aplicación de la siguiente expresión con cada uno de los datos de la matriz del audio mono:
$y[i]=x[i]-Lowpass[i]$
Donde:
Para disminuir el ruido de alta frecuencia es posible optar por aplicar un filtro de paso bajo a una frecuencia de corte determinada.
La relación de la frecuencia de corte con el factor $\alpha$ de filtrado está dada por la ecuación:
$\alpha=\frac {2*\pi*\Delta t*f_c}{(2*math.pi*\Delta t*f_c)+1}$
Donde:
$α$=Factor de filtrado
$Δt$=Cambio de tiempo ($Δt=\frac{1}{Sample\_rate}$)
$f_c$=Frecuencia de corte
fr=int(input("Especifique la frecuencia de corte en Hz "))
Frequency_Cutoff("low",fr,"hfnoise.wav","limpieza.wav")
rate_1,data_1=ReadAudio("hfnoise.wav")
data_1=ConvertToMono(data_1)
rate_2,data_2=ReadAudio("limpieza.wav")
data_2=ConvertToMono(data_2)
FFT_Graphing("FFT Con Ruido VS Lowpass fc",data_1,rate_1,"Audio sin filtrar",data_2,rate_2,f"Lowpass {fr} Hz")
AudioGraphing("Con Ruido VS Lowpass fc",data_1,rate_1,"Audio sin filtrar",data_2,rate_2,f"Lowpass {fr} Hz")
En la primera gráfica donde encontramos la transformada de fourier del audio mono es posible apreciar una atenuación considerable de la apmplitud de las frecuencias más altas y en el segundo gráfico la eliminación de los picos que representa el ruido agregando un poco de suavidad a la señal original.
playAudio("hfnoise.wav")
playAudio('limpieza.wav')
La aplicación de este filtro es posible verla de manera más clara usando un conjunto de datos con ruido de alta frecuencia.
Noisy_Data=[7729, 7330, 10075, 10998, 11502, 11781, 12413, 12530, 14070, 13789, 18186, 14401, 16691, 16654,
17424, 21104, 17230, 20656, 21584, 21297, 19986, 20808, 19455, 24029, 21455, 21350, 19854, 23476,
19349, 16996, 20546, 17187, 15548, 9179, 8586, 7095, 9718, 5148, 4047, 3873, 4398, 2989, 3848,
2916, 1142, 2427, 250, 2995, 1918, 4297, 617, 2715, 1662, 1621, 960, 500, 2114, 2354, 2900, 4878,
8972, 9460, 11283, 16147, 16617, 16778, 18711, 22036, 28432, 29756, 24944, 27199, 27760, 30706,
31671, 32185, 32290, 30470, 32616, 32075, 32210, 28822, 30823, 29632, 29157, 31585, 24133, 23245,
22516, 18513, 18330, 15450, 12685, 11451, 11280, 9116, 7975, 8263, 8203, 4641, 5232, 5724, 4347,
4319, 3045, 1099, 2035, 2411, 1727, 852, 1134, 966, 2838, 6033, 2319, 3294, 3587, 9076, 5194, 6725,
6032, 6444, 10293, 9507, 10881, 11036, 12789, 12813, 14893, 16465, 16336, 16854, 19249, 23126, 21461,
18657, 20474, 24871, 20046, 22832, 21681, 21978, 23053, 20569, 24801, 19045, 20092, 19470, 18446,
18851, 18210, 15078, 16309, 15055, 14427, 15074, 10776, 14319, 14183, 7984, 8344, 7071, 9675, 5985,
3679, 2321, 6757, 3291, 5003, 1401, 1724, 1857, 2605, 803, 2742, 2971, 2306, 3722, 3332, 4427, 5762,
5383, 7692, 8436, 13660, 8018, 9303, 10626, 16171, 14163, 17161, 19214, 21171, 17274, 20616, 18281,
21171, 18220, 19315, 22558, 21393, 22431, 20186, 24619, 21997, 23938, 20029, 20694, 20648, 21173, 20377,
19147, 18578, 16839, 15735, 15907, 18059, 12111, 12178, 11201, 10577, 11160, 8485, 7065, 7852, 5865, 4856,
3955, 6803, 3444, 1616, 717, 3105, 704, 1473, 1948, 4534, 5800, 1757, 1038, 2435, 4677, 8155, 6870, 4611,
5372, 6304, 7868, 10336, 9091]
#Convertimos los datos en un array de numpy de un canal
Noisy_Data=np.array(Noisy_Data,dtype=np.int16)
data_rate=1
#Filtramos la señal con un filtro de paso bajo
filtered=Lowpass(Noisy_Data,alpha=0.2)
#Graficamos la señal de forma comparativa
FFT_Graphing("FFT señal con ruido vs filtrada",Noisy_Data,data_rate,"Señal con ruido",filtered,data_rate,"Señal filtrada")
AudioGraphing("Señal con ruido vs filtrada",Noisy_Data,data_rate,"Señal con ruido",filtered,data_rate,"Señal filtrada")
En el último gráfico es posible ver con más detalle la eliminación del ruido de alta frecuencia donde se ve una señal suavizada y sin picos. Por otra parte en la primera gráfica donde se encuentra la Transformada de Fourier es posible apreciar la atenuación de las frecuencias más altas las cuales representan el ruido.
Para la ecualización de las frecuencias altas y bajas del audio, usamos los filtros de paso bajo y paso alto sobre la canción. La variación del resultado de la ecualización estará dado por el cambio en el factor $\alpha$.
alpha = 0.2 #@param {type:"slider", min:0, max:1, step:0.01}
rate,data=ReadAudio("Happy.wav")
data=ConvertToMono(data)
data_low=Lowpass(data,alpha)
data_high=Highpass(data,alpha)
FFT_Graphing(f"FFT Original vs Low pass α={alpha}",data,rate,"Original",data_low,rate,"Lowpass")
AudioGraphing(f"Original vs Low pass α={alpha}",data,rate,"Original",data_low,rate,"Lowpass")
FFT_Graphing(f"FFT Original vs High pass α={alpha}",data,rate,"Original",data_high,rate,"Highpass")
AudioGraphing(f"Original vs High pass α={alpha}",data,rate,"Original",data_high,rate,"Highpass")
Dado que la señal es una canción y no tiene ruido anormal, se presenta una atenuación normal de las frecuencias altas y bajas según corresponde la aplicación del filtro. Por otra parte es pobile apreciar una conservación apreciable de la señal original con la aplicación del filtro de paso bajo que se muestra en la primera gráfica, lo cual sugiere que el audio presenta frecuencias bajas y el factor de filtrado $\alpha$ no es lo suficientemente pequeño para una atenuación más amplia en el espectro del audio.
print(f"Lowpass α={alpha}")
WriteAudio("lowpass.wav",rate,data_low)
playAudio("lowpass.wav")
print(f"Highpass α={alpha}")
WriteAudio("highpass.wav",rate,data_high)
playAudio("highpass.wav")
Combinar_Audios("Happy.wav","sweet.wav","Combined.wav")
Nota: Aumentar el volumen amplifica también aumentar la amplitud del ruido de alta frecuencia, un volumen mayor a 150 que el rudio de alta frecuencia se haga más audible, según el audio elegido puede haber menor o mayor aumento de este ruido. Por ello se limita, por el momento hasta 150%, el aumento de volumen, pues en caso contrario habría que también filtrar el audio de ruido de alta frecuencia.
Volumen=120 #@param {type:"slider", min:0, max:150, step:1}
AdjustVolume("sweet.wav",Volumen,"up.wav")
En la siguiente gráfica, es posible ver en correspondencia este aumento de la amplitud del audio de salida:
rate_1,data_1=ReadAudio("sweet.wav")
data_1=ConvertToMono(data_1)
rate_2,data_2=ReadAudio("up.wav")
AudioGraphing(f"Original vs Volumen {Volumen}%",data_2,rate_2,f"Volumen al {Volumen}%",data_1,rate_1,"Volumen original")
Volumen_2=5 #@param {type:"slider", min:0, max:150, step:1}
AdjustVolume("sweet.wav",Volumen_2,"down.wav")
En la siguiente gráfica, es posible ver en correspondencia esta reducción de la amplitud del audio de salida:
rate_a,data_a=ReadAudio("sweet.wav")
data_a=ConvertToMono(data_1)
rate_b,data_b=ReadAudio("down.wav")
AudioGraphing(f"Original vs Volumen {Volumen_2}%",data_a,rate_a,"Volumen original",data_b,rate_b,f"Volumen al {Volumen_2}%")
High Pass Filter y Low Pass Filter (HPF – LPF) – Soundsystem.es. (2018). Retrieved 7 July 2020, from https://soundsystems.es/high-pass-filter-y-low-pass-filter-hpf-lpf/
John Paul, M., & Luca, M. Performing a Fast Fourier Transform (FFT) on a Sound File - dummies. Retrieved 7 July 2020, from https://www.dummies.com/programming/python/performing-a-fast-fourier-transform-fft-on-a-sound-file/
Llamas, L. (2017). Filtro paso bajo y paso alto exponencial (EMA) en Arduino. Retrieved 7 July 2020, from https://www.luisllamas.es/arduino-paso-bajo-exponencial/
Lowpass Filter. Wikipedia. Retrieved 7 July 2020, from https://en.wikipedia.org/wiki/Low-pass_filter
Sainburg, T. (2018). Noise reduction using spectral gating in python. Retrieved 7 July 2020, from https://timsainburg.com/noise-reduction-python.html#noise-reduction-python
The Simplest Lowpass Filter | Introduction to Digital Filters. Retrieved 7 July 2020, from https://www.dsprelated.com/freebooks/filters/Simplest_Lowpass_Filter_I.html
Venturini, G. (2019). Todos los formatos de audio - Tecnología + Informática. Retrieved 7 July 2020, from https://www.tecnologia-informatica.com/todos-los-formatos-de-audio/